#include "ringbuffer.h"
#include "common.h"

static int read_pesudo(struct ringbuffer *ring, char *buff, int size);
static int read_out(struct ringbuffer *ring, char *buff, int size);
static int write_in(struct ringbuffer *ring, char *buff, int size);
static int reset(struct ringbuffer *ring);
static int getdatalen(struct ringbuffer *ring);
static void lock(struct ringbuffer *ring);
static void unlock(struct ringbuffer *ring);
static bool isbusy(struct ringbuffer *ring);

static int read_pesudo(struct ringbuffer *ring, char *buff, int size)
{
    int bottom = 0;

    if (0 == ring->data_size) {
        return EMPTY;
    }

    if (size > ring->data_size) {
        size = ring->data_size;
    }
    
    bottom = (ring->buff + ring->buff_size) - ring->front;

    if (size < bottom) {
        memcpy(buff, ring->front, size);
    } else {
        memcpy(buff, ring->front, bottom);    
        memcpy(buff + bottom, ring->buff, size - bottom);    
    }

    return size; 
}

static int read_out(struct ringbuffer *ring, char *buff, int size)
{
    int bottom = 0;

    if (0 == ring->data_size) {
        return EMPTY;
    }

    if (size > ring->data_size) {
        size = ring->data_size;
    }
    
    bottom = (ring->buff + ring->buff_size) - ring->front;

    if (size < bottom) {
        if (!(ring->isbusy)(ring)) {
            (ring->lock)(ring);
            memcpy(buff, ring->front, size);
            ring->front += size;
            ring->data_size -= size;
            (ring->unlock)(ring);
        } else {
            return BUSY;
        }
    } else {
        if (!(ring->isbusy)(ring)) {
            (ring->lock)(ring);
            memcpy(buff, ring->front, bottom);    
            ring->front = ring->buff;
            memcpy(buff + bottom, ring->front, size - bottom);    
            ring->front += (size - bottom); 
            ring->data_size -= size;
            (ring->unlock)(ring);
        } else {
            return BUSY;
        }
    }

    return size;
}

static int write_in(struct ringbuffer *ring, char *buff, int size)
{
    int bottom = 0;

    if (size > (ring->buff_size - ring->data_size)) {
        return OVERFLOW; 
    }

    bottom = (ring->buff + ring->buff_size) - ring->trail;
    
    if (size < bottom) {
        if (!(ring->isbusy)(ring)) {
            (ring->lock)(ring);
            memcpy(ring->trail, buff, size);    
            ring->trail += size;
            ring->data_size += size;
            (ring->unlock)(ring);
        } else {
            return BUSY;
        }
    } else {
        if (!(ring->isbusy)(ring)) {
            (ring->lock)(ring);
            memcpy(ring->trail, buff, bottom);    
            ring->trail = ring->buff;
            memcpy(ring->trail, buff + bottom, size - bottom);    
            ring->trail += (size - bottom);
            ring->data_size += size;
            (ring->unlock)(ring);
        } else {
            return BUSY;
        }
    }

    return size;
}

static int getdatalen(struct ringbuffer *ring)
{
    return ring->data_size;
}

static int reset(struct ringbuffer *ring)
{
    memset(ring->buff, 0, ring->buff_size);  
    ring->front = ring->buff;
    ring->trail = ring->buff;
    ring->data_size = 0;

    return OKAY;
}

static void lock(struct ringbuffer *ring)
{
    pthread_mutex_lock(&ring->bufflock);
}

static void unlock(struct ringbuffer *ring)
{
    pthread_mutex_unlock(&ring->bufflock);
}

static bool isbusy(struct ringbuffer *ring)
{
    if (pthread_mutex_trylock(&ring->bufflock) == EBUSY) {
        return true;
    } else {
        pthread_mutex_unlock(&ring->bufflock);
        return false;
    }
}

struct ringbuffer* ringbuffer_create(int size)
{
    struct ringbuffer *ring = (struct ringbuffer *)malloc(sizeof(struct ringbuffer)); 
    ring->buff = (char *)malloc(size);
    ring->buff_size = size;
    ring->data_size = 0; 
    pthread_mutex_init(&ring->bufflock, NULL);

    ring->front = ring->buff;
    ring->trail = ring->buff;
    ring->reset = reset;
    ring->read_pesudo = read_pesudo;
    ring->read_out = read_out;
    ring->write_in = write_in;
    ring->isbusy = isbusy;
    ring->lock = lock;
    ring->unlock = unlock;

    return ring;
}

void ringbuffer_free(struct ringbuffer *ring)
{
    free(ring->buff);
    free(ring);
}

int ring_readout(struct ringbuffer *ring, char *buff, int size)
{
    if (ring != NULL) {
        return (ring->read_out)(ring, buff, size);
    } else {
        return ERROR;
    }
}

int ring_readpesudo(struct ringbuffer *ring, char *buff, int size)
{
    if (ring != NULL) {
        return (ring->read_pesudo)(ring, buff, size);
    } else {
        return ERROR;
    }
}

int ring_writein(struct ringbuffer *ring, char *buff, int size)
{
    if (ring != NULL) {
        return (ring->write_in)(ring, buff, size);
    } else {
        return ERROR;
    }
}

int ring_reset(struct ringbuffer *ring)
{
    if (ring != NULL) {
        return (ring->reset)(ring);
    } else {
        return ERROR;
    }
}

int ring_dump(struct ringbuffer *ring, int size)
{
    int i;
    printf("Ring buffer info:\n");

    printf("buffaddr: %4x, buffsize: %4d, datasize:%4d,"
           " front: %8x; trail: %8x, lock: %d.\n",
           ring->buff, 
           ring->buff_size, 
           ring->data_size, 
           ring->front, 
           ring->trail,
           ring->bufflock);

    for (i = 0; i < size; i++) {
        if ( i == 0 || i % 8 == 0) {
            printf("\n");
            printf("0x%4x: ", ring->buff + i);
        }

        if ((i !=0) && (i % 8 == 0)) {
            //printf("\n");
        }

        printf("0x%02x ", ring->buff[i]);
    }

    printf("\n\n");
}
